class_name StateChartStateActionBase
extends Node2D

enum ActionTiming
{
	Init,
	EventReceived,
	Entered,
	Exited,
	Input,
	UnhandledInput,
	Processing,
	PhysicsProcessing,
	Stepped,
	TransitionPending,
}
#region 属性
@export var state_name:StringName
## 冷却时间
@export var cooling_time:float = 0
@export var additional_effects:Array[AdditionalEffectInfo_Action]
var manager:StateChartManager
var state:State
var connections:Dictionary = {}
var additional_effect_dict:Dictionary = {}
var addition_effect_init_args:Dictionary = {}
var state_event_callback_dict:Dictionary = {}
#endregion
#region 字段
#endregion
#region 信号
signal state_event(action_timing:ActionTiming)
#endregion

#region 基类方法
#endregion
#region 公共方法
func init(_manager:StateChartManager,_state:State):
	manager = _manager
	state = _state
	state.get_state_status_func = get_state_status
	connections["event_received"] = ConnectObj.new(state.event_received,[on_event_received])
	connections["state_entered"] = ConnectObj.new(state.state_entered,[on_state_entered])
	connections["state_exited"] = ConnectObj.new(state.state_exited,[on_state_exited])
	connections["state_input"] = ConnectObj.new(state.state_input,[on_state_input])
	connections["state_unhandled_input"] = ConnectObj.new(state.state_unhandled_input,[on_state_unhandled_input])
	connections["state_processing"] = ConnectObj.new(state.state_processing,[on_state_processing])
	connections["state_physics_processing"] = ConnectObj.new(state.state_physics_processing,[on_state_physics_processing])
	connections["state_stepped"] = ConnectObj.new(state.state_stepped,[on_state_stepped])
	connections["transition_pending"] = ConnectObj.new(state.transition_pending,[on_transition_pending])
	
	addition_effect_init_args.set("Target",manager.get_entity())
	addition_effect_init_args.set("Owner",self)
	for info:AdditionalEffectInfo_Action in additional_effects:
		additional_effect_dict.get_or_add(info.timing,[]).append_array(info.addition_effects)
	for key in ActionTiming.values():
		for effect:AdditionalEffect in additional_effect_dict.get(key,[]):
			effect.init(addition_effect_init_args)
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.Init,[]):
		effect.excute()
	state_event.emit(ActionTiming.Init)
func on_event_received(event:StringName):
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.EventReceived,[]):
		effect.excute()
	state_event.emit(ActionTiming.EventReceived)
func on_state_entered():
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.Entered,[]):
		effect.excute()
	state_event.emit(ActionTiming.Entered)
func on_state_exited():
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.Exited,[]):
		effect.excute()
	state_event.emit(ActionTiming.Exited)
func on_state_input(event:InputEvent):
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.Input,[]):
		effect.excute()
	state_event.emit(ActionTiming.Input)
func on_state_unhandled_input(event:InputEvent):
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.UnhandledInput,[]):
		effect.excute()
	state_event.emit(ActionTiming.UnhandledInput)
func on_state_processing(delta:float):
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.Processing,[]):
		effect.excute()
	state_event.emit(ActionTiming.Processing)
func on_state_physics_processing(delta:float):
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.PhysicsProcessing,[]):
		effect.excute()
	state_event.emit(ActionTiming.PhysicsProcessing)
func on_state_stepped():
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.Stepped,[]):
		effect.excute()
	state_event.emit(ActionTiming.Stepped)
func on_transition_pending(init_delay:float,remaining_delay:float):
	for effect:AdditionalEffect in additional_effect_dict.get(ActionTiming.TransitionPending,[]):
		effect.excute()
	state_event.emit(ActionTiming.TransitionPending)
func register_state_event(action_timing:ActionTiming,callback:Callable,connect_flag:ConnectFlags = 0):
	if not state_event_callback_dict.has(callback):
		var final_callback = func(cur_action_timing:ActionTiming):
			if action_timing == cur_action_timing:
				callback.call()
				if connect_flag == ConnectFlags.CONNECT_ONE_SHOT:
					unregister_state_event(action_timing,callback)
		state_event_callback_dict[callback] = final_callback
		state_event.connect(final_callback)
func unregister_state_event(action_timing:ActionTiming,callback:Callable):
	if state_event_callback_dict.has(callback) and state_event.is_connected(state_event_callback_dict[callback]):
		state_event.disconnect(state_event_callback_dict[callback])
		state_event_callback_dict.erase(callback)
## 得到当前State状态，用于判断能否转换
func get_state_status() -> GameEnum.StateStatus:
	var time_check = true
	if cooling_time > 0 and state.enter_tick < 0 and state.exit_tick > 0:
		time_check = state.exit_tick + cooling_time * 1000 <= Time.get_ticks_msec()
	return GameEnum.StateStatus.Ready if time_check else GameEnum.StateStatus.Closed
#endregion
#region 私有方法
func _get_manager() -> StateChartManager:
	if manager:
		return manager
	else:
		var parent = get_parent()
		while parent:
			if parent is StateChartManager:
				manager = parent
				return manager
			else:
				parent = parent.get_parent()
		return null
func _on_anim_key_event(args:Dictionary):
	for additional_effect in args["AdditionalEffects"]:
			additional_effect.excute()
#endregion
#region 生命周期函数
func _ready() -> void:
	if Engine.is_editor_hint():
		if not additional_effect_dict:
			additional_effect_dict = {}
		for timing in ActionTiming.keys():
			var arr:Array[AdditionalEffect] = []
			additional_effect_dict.get_or_add(timing,arr)
func _notification(what: int) -> void:
	if what == NOTIFICATION_PREDELETE:
		for connection:ConnectObj in connections.values():
			connection.disconnect_all()
			connections.clear()
func _get_configuration_warnings() -> PackedStringArray:
	var warnings = []
	var m = _get_manager()
	if get_parent().name != "StateActions":
		warnings.append("这个节点必须在StateActions命名的节点下")
	if not m:
		warnings.append("这个节点必须是StateChartManager的孙节点")
	return warnings
#endregion
